Skip to content

feat: multiPCS infra for Ultra and Mega#20230

Open
iakovenkos wants to merge 62 commits intonextfrom
si/multipcs-proto
Open

feat: multiPCS infra for Ultra and Mega#20230
iakovenkos wants to merge 62 commits intonextfrom
si/multipcs-proto

Conversation

@iakovenkos
Copy link
Copy Markdown
Contributor

@iakovenkos iakovenkos commented Feb 6, 2026

Summary

Adds parameterized polynomial interleaving to Mega and Ultra proving systems. Instead of committing to each polynomial individually (BS = 1), polynomials are grouped into interleaved commitments of size BS = 2 or BS = 4, reducing the number of commitments and shrinking the recursive verifier circuit—at the cost of additional PCS work.

Key changes

  • Templatize MegaFlavor_<BS> and UltraFlavor_<BS> on interleaving batch size
  • Add BS = 2 (DualMegaFlavor, DualUltraFlavor) and BS = 4 (MultiMegaFlavor) with full ZK and recursive flavor variants
  • Unified BS-agnostic OinkProver / OinkVerifier — no branching on batch size
  • Memory-efficient on-the-fly interleaving via strided polynomial views
  • commit_interleaved<BS> in commitment key for batched Pippenger
  • Gemini generalized for arbitrary shift exponent (G / X^k where k = BS)
  • Repeated commitment deduplication for all batch sizes

Prover Benchmarks (8 cores, wall time)

┌────────────────┬─────────┬──────────┬───────────┬───────────────┬───────────────┐
│     Flavor     │  2^19   │   2^20   │  vs BS=1  │ Peak RSS 2^19 │ Peak RSS 2^20 │
├────────────────┼─────────┼──────────┼───────────┼───────────────┼───────────────┤
│ Ultra BS=1     │ 3516 ms │  6423 ms │ —         │        663 MB │       1327 MB │
├────────────────┼─────────┼──────────┼───────────┼───────────────┼───────────────┤
│ DualUltra BS=2 │ 4722 ms │  8648 ms │ +34-35%   │        698 MB │       1393 MB │
├────────────────┼─────────┼──────────┼───────────┼───────────────┼───────────────┤
│ Mega BS=1      │ 3519 ms │  6420 ms │ —         │        656 MB │       1329 MB │
├────────────────┼─────────┼──────────┼───────────┼───────────────┼───────────────┤
│ DualMega BS=2  │ 4738 ms │  8795 ms │ +35-37%   │        694 MB │       1377 MB │
├────────────────┼─────────┼──────────┼───────────┼───────────────┼───────────────┤
│ MultiMega BS=4 │ 7305 ms │ 13664 ms │ +108-113% │        834 MB │       1771 MB │
└────────────────┴─────────┴──────────┴───────────┴───────────────┴───────────────┘

Prover overhead is dominated by additional Gemini fold rounds on BS * n-sized group buffers. Memory overhead is modest (~5% for BS = 2, ~27–33% for BS = 4).


Recursive Verifier Circuit Size (UltraCircuitBuilder, DefaultIO)

┌──────────────────┬─────────┬─────────┐
│      Flavor      │  Gates  │ vs BS=1 │
├──────────────────┼─────────┼─────────┤
│ Ultra BS=1       │ 694,088 │ —       │
├──────────────────┼─────────┼─────────┤
│ UltraZK BS=1     │ 737,938 │ +6%     │
├──────────────────┼─────────┼─────────┤
│ DualUltra BS=2   │ 557,821 │ -20%    │
├──────────────────┼─────────┼─────────┤
│ DualUltraZK BS=2 │ 598,136 │ -14%    │
├──────────────────┼─────────┼─────────┤
│ Mega BS=1        │ 811,478 │ —       │
├──────────────────┼─────────┼─────────┤
│ MegaZK BS=1      │ 781,918 │ -4%     │
├──────────────────┼─────────┼─────────┤
│ DualMega BS=2    │ 600,237 │ -26%    │
├──────────────────┼─────────┼─────────┤
│ DualMegaZK BS=2  │ 593,287 │ -27%    │
├──────────────────┼─────────┼─────────┤
│ MultiMega BS=4   │ 507,614 │ -37%    │
├──────────────────┼─────────┼─────────┤
│ MultiMegaZK BS=4 │ 479,366 │ -41%    │
└──────────────────┴─────────┴─────────┘

Test plan

  • ultra_honk_testsDualUltra / DualUltraZK added to UltraHonkTests, lookup, and rom_ram suites
  • ultra_honk_testsDualMega / DualMegaZK / MultiMega added to MegaHonkTests (prove/verify, interleaved storage, transcript manifest)
  • stdlib_honk_verifier_tests — recursive verification for all DualUltra / DualMega / MultiMega flavors with UltraCircuitBuilder
  • Repeated commitment deduplication verified for all batch sizes

iakovenkos and others added 14 commits February 4, 2026 00:09
ZK support via MultiMegaZKFlavor with masking polynomials, Libra
commitments, and SmallSubgroupIPA. Recursive verification for both
MultiMega and MultiMegaZK with Ultra and Mega outer builders, including
proof_length specializations for recursive flavors.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Reorder InterleavedWitnessCommitments so unshiftable groups come first
and the 3 shiftable groups (W1, W6, W9) are contiguous at the end,
enabling the existing remove_repeated_commitments mechanism to merge
shifted/unshifted commitment scalars. This saves 3 points from the
final PCS MSM.

Key changes:
- Reorder InterleavedWitnessCommitments_ members and get_unshifted_groups
- Enable REPEATED_COMMITMENTS in MultiMega and MultiMegaZK flavors
- Update FINAL_PCS_MSM_SIZE to reflect the reduced MSM size
- Alias InterleavedWitnessCommitments/PrecomputedCommitments in
  recursive flavor from native to avoid duplication
- Simplify remove_repeated_commitments: remove has_zk parameter,
  use absolute indices from RepeatedCommitmentsData directly
- Pre-bake Shplemini offset into REPEATED_COMMITMENTS indices across
  all flavors (Ultra, Mega, ECCVM, Translator, MultiMega + ZK variants)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@iakovenkos iakovenkos changed the title chore: multi-chonk prototype chore: add MegaFlavor using multiPCS Feb 18, 2026
@iakovenkos iakovenkos changed the title chore: add MegaFlavor using multiPCS chore: add MegaFlavors using multiPCS Feb 18, 2026
@iakovenkos iakovenkos marked this pull request as ready for review February 18, 2026 13:26
iakovenkos and others added 24 commits March 16, 2026 16:25
…d translator ordering

- MegaZKFlavor BS=1: HasGeminiMasking=false, no masking entities, -1 REPEATED_COMMITMENTS shift
- oink prover/verifier: use flavor_has_gemini_masking gate for masking commitment
- batched translator verifier: translator-first unshifted ordering, no MegaZK masking
- honk_transcript test: use flavor_has_gemini_masking for manifest gate
- ultra_verifier: use flavor_has_gemini_masking in build_pcs_commitments
Replace persistent group buffers with on-the-fly interleaved polynomial
construction. Entities are allocated individually (same memory as BS=1).
Interleaved buffers are built transiently for commitment and PCS.

Key changes:
- Extract rho from Gemini/Shplemini into prover call sites
- Add pippenger_interleaved + commit_interleaved for buffer-free MSM
- BS>1 PCS: pre-batch groups into F/G with rho, free entities, manual Gemini
- VK construction uses commit_interleaved (no interleaved buffers)
- Remove group_buffers_, allocate_interleaved_groups, group_buffer_for

Peak memory at 2^19 BS=4: 825 MiB (was 1848 MiB with group buffers).
Replace all if-constexpr BS branching in oink with unified group-based
iteration using OinkWitnessRounds_<BS> descriptors and commit_interleaved<BS>.

- Add OinkWitnessRounds_<1> and OinkWitnessRounds_<4> with per-round group
  descriptors (entity pointers + labels) for wires, lookup, inverses, z_perm
- Add OinkGroupDescriptor<Ptr> as the shared descriptor type
- commit_interleaved<1> delegates to commit() (no temp buffer)
- Oink prover: commit_round_groups iterates groups uniformly for all BS
- Oink verifier: receive_round_groups iterates groups uniformly for all BS
- Remove CommitmentLabels and InterleavedCommitmentLabels from oink
- Adding BS=2 requires only OinkWitnessRounds_<2> — zero oink code changes
Add MegaFlavor_<2> (DualMegaFlavor) with groups of 2 polynomials per
interleaved commitment: 16 precomputed groups, 15 witness groups (4
shiftable), INTERLEAVING_LOG_K=1. Includes ZK variant, recursive
flavors, all template instantiations, and test coverage.
Templatize UltraFlavor_ and UltraZKFlavor_ on BATCH_SIZE, mirroring
MegaFlavor_. Add UltraFlavor_<2> (DualUltraFlavor) with 14 precomputed
groups, 5 witness groups (3 shiftable), INTERLEAVING_LOG_K=1. Includes
ZK variant, recursive flavors, template instantiations, and test
coverage.
@iakovenkos iakovenkos changed the title chore: add MegaFlavors using multiPCS feat: multiPCS infra for Ultra and Mega Mar 19, 2026
Review fixes:
- info() → vinfo() in ultra_verifier.cpp
- add DualUltra flavors to IsUltraHonk Starknet branch
- fix stale comments in claim_batcher.hpp and mega_zk_flavor.hpp
- add missing get_shiftable() const to BS=2/BS=4 ZK witness commitments
- hoist r_inv_shift computation in gemini.hpp
- restore static_assert(MAX_PARTIAL_RELATION_LENGTH == 7)

Test coverage:
- add DualUltraFlavor + DualUltraZKFlavor to UltraHonkTests type list
  (also rom_ram.test.cpp and lookup.test.cpp for consistent type lists)
- remove standalone DualUltraHonkTests::Basic (now covered by shared suite)
- skip ANonZeroPolynomialIsAGoodPolynomial for BS>1 (strided views)
@iakovenkos iakovenkos self-assigned this Mar 19, 2026
Copy link
Copy Markdown
Contributor

@federicobarbacovi federicobarbacovi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! A lot of comments but nothing major. Thanks for this!

}

// Add hiding op (required before creating the builder)
eccvm_test_utils::add_hiding_op_for_test(op_queue);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WHy was this not needed before?

*/
template <typename Flavor>
template <typename PolyDescs, typename CommDescs>
void OinkProver<Flavor>::commit_round_groups(const PolyDescs& poly_groups,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

static constexpr size_t INTERLEAVING_LOG_K = (BATCH_SIZE_ <= 1) ? 0
: (BATCH_SIZE_ <= 2) ? 1
: (BATCH_SIZE_ <= 4) ? 2
: 3;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we ever hit the 3 case?

* @brief ZK-specific masking entities, specialized per (DataType, BATCH_SIZE, HasZK).
*
* - (any BS, HasZK=false): empty
* - (BS=1, HasZK=true): single gemini_masking_poly
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're missing BS=2 here and in various other places in the comments

interleaved_precomputed_4, // P₅: [sigma_2, sigma_3, sigma_4, id_1]
interleaved_precomputed_5, // P₆: [id_2, id_3, id_4, table_1]
interleaved_precomputed_6, // P₇: [table_2, table_3, table_4, lagrange_first]
interleaved_precomputed_7) // P₈: [lagrange_last, lagrange_ecc_op, databus_id] (3 polys)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a zero at the end of this group

const size_t total_size = n * batch_size;

// Build interleaved scalar array: for logical index i, place chunk_j at position batch_size*i + j.
std::vector<Fr> interleaved_scalars(total_size, Fr::zero());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the function description you say you don't materialize the interleaved buffer but then you construct this vector, aren't these two statements in contradiction?

EXPECT_EQ(cc.size(), 1);

// Expected variables in one gate:
// TODO: MultiMega oink verifier receives 4 individual ecc_op_wire commitments for merge protocol
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a runaway comment. However, what is correct is that we wouldn't be able to set Flavor = DualMegaFlavor in Chonk at the moment because the Merge is not integrated with it, maybe it's worth pointing out

batched_unshifted = std::move(extended);
}
if (!batched_shifted_tail_.is_empty()) {
batched_to_be_shifted += batched_shifted_tail_;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this not need to be extended?

batched_to_be_shifted *= r_inv_shift; // G = G/r^k

A_0_pos += batched_to_be_shifted; // A₀₊ += G/r^k
// A₀₋(X) = F(X) + (-1)^k · G(X)/r^k so that A₀₋(-r) = A₀(-r)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// A₀₋(X) = F(X) + (-1)^k · G(X)/r^k so that A₀₋(-r) = A₀(-r)
// A₀₋(X) = F(X) + (-1)^k · G(X)/r^k since (-r)^k = (-1)^k · r^k. For odd k: subtract. For even k: add.

MEGA_ZK_ORIG, // MegaZK original: witness start in unshifted
MEGA_ZK_DUP, // MegaZK duplicate: mega_shifted start in shifted
S, // MegaZK count
2 /* shplemini_offset: Q + translator_masking_poly */);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the 2 coming from?

Base automatically changed from merge-train/barretenberg to next March 24, 2026 00:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants